package wortubung.util;

import java.util.HashMap;
import java.util.HashSet;

public class LatinScript {
	public static final char GRAVE  = '`';
    public static final char ACUTE  = '\'';
    public static final char CIRC   = '^';
    public static final char TILDE  = '~';
    public static final char UMLAUT = ':';
    public static final char RING   = '.';
    public static final char CARON  = '[';
    public static final char CEDIL  = ',';
    public static final char SLASH  = '/';
    public static final char MACRON = '-';

	private static final String[] ligatures = {
		"AE\u00C6", "ae\u00E6", "OE\u0152", "oe\u0153", // "ss\u00DF"
	};

    private static char[][] diacriticalMap = {
    //   	  grave  acute  circ   tilde  umlaut ring   slash  caron  cedil  macron
    /*A*/	{ 192,   193,   194,   195,   196,   197,   'A',   461,   'A',   256  },
    /*C*/	{ 'C',   'C',   264,   'C',   'C',   'C',   'C',   268,   199,   'C'  },
    /*D*/	{ 'D',   'D',   'D',   'D',   'D',   'D',   'D',   270,   'D',   'D'  },
    /*E*/	{ 200,   201,   202,   'E',   203,   'E',   'E',   282,   'E',   274  },
    /*G*/	{ 'G',   'G',   284,   'G',   'G',   'G',   'G',   'G',   'G',   'G'  },
    /*H*/	{ 'H',   'H',   292,   'H',   'H',   'H',   'H',   'H',   'H',   'H'  },
    /*I*/	{ 204,   205,   206,   296,   207,   'I',   'I',   463,   'I',   298  },
    /*J*/	{ 'J',   'J',   308,   'J',   'J',   'J',   'J',   'J',   'J',   'J'  },
    /*N*/	{ 'N',   'N',   'N',   209,   'N',   'N',   'N',   327,   'N',   'N'  },
    /*O*/	{ 210,   211,   212,   213,   214,   'O',   216,   465,   'O',   332  },
    /*R*/	{ 'R',   'R',   'R',   'R',   'R',   'R',   'R',   344,   'R',   'R'  },
    /*S*/	{ 'S',   'S',   348,   'S',   'S',   'S',   'S',   352,   'S',   'S'  },
    /*T*/	{ 'T',   'T',   'T',   'T',   'T',   'T',   'T',   356,   'T',   'T'  },
    /*U*/	{ 217,   218,   219,   360,   220,   366,   'U',   364,   'U',   362  },
    /*Y*/	{ 'Y',   221,   'Y',   'Y',   376,   'Y',   'Y',   'Y',   'Y',   'Y'  },
    /*Z*/	{ 'Z',   'Z',   'Z',   'Z',   'Z',   'Z',   'Z',   381,   'Z',   'Z'  },
    /*a*/	{ 224,   225,   226,   227,   228,   229,   'a',   462,   'a',   257  },
    /*c*/	{ 'c',   'c',   265,   'c',   'c',   'c',   'c',   269,   231,   'c'  },
    /*d*/	{ 'd',   'd',   'd',   'd',   'd',   'd',   'd',   271,   'd',   'd'  },
    /*e*/	{ 232,   233,   234,   'e',   235,   'e',   'e',   283,   'e',   275  },
    /*g*/	{ 'g',   'g',   285,   'g',   'g',   'g',   'g',   'g',   'g',   'g'  },
    /*h*/	{ 'h',   'h',   293,   'h',   'h',   'h',   'h',   'h',   'h',   'h'  },
    /*i*/	{ 236,   237,   238,   297,   239,   'i',   'i',   464,   'i',   299  },
    /*j*/	{ 'j',   'j',   309,   'j',   'j',   'j',   'j',   'j',   'j',   'j'  },
    /*n*/	{ 'n',   'n',   'n',   241,   'n',   'n',   'n',   328,   'n',   'n'  },
    /*o*/	{ 242,   243,   244,   245,   246,   'o',   248,   466,   'o',   333  },
    /*r*/	{ 'r',   'r',   'r',   'r',   'r',   'r',   'r',   345,   'r',   'r'  },
    /*s*/	{ 's',   's',   349,   's',   's',   's',   's',   353,   's',   's'  },
    /*t*/	{ 't',   't',   't',   't',   't',   't',   't',   357,   't',   't'  },
    /*u*/	{ 249,   250,   251,   361,   252,   367,   'u',   365,   'u',   363  },
    /*y*/	{ 'y',   253,   'y',   'y',   255,   'y',   'y',   'y',   'y',   'y'  },
    /*z*/	{ 'z',   'z',   'z',   'z',   'z',   'z',   'z',   382,   'z',   'z'  },
    };
    private static char[] accentMapYIndex = {
    	GRAVE, ACUTE, CIRC, TILDE, UMLAUT, RING, SLASH, CARON, CEDIL, MACRON
    };
    private static char[] accentMapXIndex = {
    	'A', 'C', 'D', 'E', 'G', 'H', 'I', 'J', 'N', 'O', 'R', 'S', 'T', 'U', 'Y', 'Z',
    	'a', 'c', 'd', 'e', 'g', 'h', 'i', 'j', 'n', 'o', 'r', 's', 't', 'u', 'y', 'z',
    };

    private static int toXIndex(char c) {
    	for (int i=accentMapXIndex.length-1; i>=0; --i)
    		if (accentMapXIndex[i] == c)
    			return i;
    	return -1;
    }

    private static int toYIndex(char c) {
    	for (int i=accentMapYIndex.length-1; i>=0; --i)
    		if (accentMapYIndex[i] == c)
    			return i;
    	return -1;
    }
    
    private static HashMap<Character,String> deaccentMap = new HashMap<Character,String>();
    private static HashSet<Character> vowelSet = new HashSet<Character>();

    static {
    	vowelSet.add('A'); vowelSet.add('a');
    	vowelSet.add('E'); vowelSet.add('e');
    	vowelSet.add('I'); vowelSet.add('i');
    	vowelSet.add('O'); vowelSet.add('o');
    	vowelSet.add('U'); vowelSet.add('u');

    	for (String lig : ligatures)
    		if (isVowel(lig.charAt(0)))
    			vowelSet.add(lig.charAt(2));

    	for (int xIndex = 0; xIndex < accentMapXIndex.length; ++xIndex) {
    		char letter = accentMapXIndex[xIndex];
        	for (int yIndex = 0; yIndex < accentMapYIndex.length; ++yIndex) {
        		char c = diacriticalMap[xIndex][yIndex];
        		if (c != letter) {
        			char accent = accentMapYIndex[yIndex];
        			deaccentMap.put(c, new String(new char[]{ accent, letter }));
        		}

        		if (vowelSet.contains(letter))
        			vowelSet.add(c);
        	}
    	}
    }

    public static void enAccent(String[] sa) {
    	if (sa != null) {
	    	for (int i=sa.length-1; i>=0; --i)
	    		sa[i] = enAccent(sa[i]);
    	}
    }

    // backslash escapes the next char
    public static String enAccent(String s) {
    	if (s == null)
    		return null;
    	StringBuilder sb = new StringBuilder();
    	int i;
    	boolean hasChange = false;
loop:
    	for (i=0; i < s.length()-1; ++i) {
    		char accent_ = s.charAt(i);
    		char letter_ = s.charAt(i+1);
    		for (String lig : ligatures) {
    			if (accent_ == lig.charAt(0) && letter_ == lig.charAt(1)) {
    				hasChange = true;
    				sb.append(lig.charAt(2));
    				++i;
    				continue loop;
    			}
    		}
    		if (accent_ == '\\') {
    			hasChange = true;
    			sb.append(letter_);
    			++i;
    			continue;
    		}
    		int yIndex = toYIndex(accent_);
    		if (yIndex < 0) {
    			sb.append(accent_);
    		} else {
    			int xIndex = toXIndex(letter_);
    			if (xIndex < 0)
    				sb.append(accent_);
    			else {
    				char c = diacriticalMap[xIndex][yIndex];
    				if (c == letter_)
    					sb.append(accent_);
    				else {
            			hasChange = true;
        				sb.append(c);
        				++i;
    				}
    			}
    		}
    	}
    	if (i == s.length()-1)
    		sb.append(s.charAt(i));
    	return hasChange ? sb.toString() : s;
    }

    public static char deAccent(char c) {
    	String accentLetter = deaccentMap.get(c);
    	if (accentLetter != null)
   			return accentLetter.charAt(accentLetter.length() == 1 ? 0 : 1);
    	return c;
    }

    public static void deAccent(String[] sa, boolean withDiacritics) {
    	if (sa == null)
    		return;
    	for (int i=sa.length-1; i>=0; --i)
    		sa[i] = deAccent(sa[i], withDiacritics);
    }

    public static String deAccent(String s) {
    	return deAccent(s, false);
    }

    public static String deAccent(String s, boolean withDiacritics) {
    	if (s == null)
    		return null;
        StringBuilder sb = new StringBuilder();
        for (int i=0; i<s.length(); i++)
            deAccent(sb, s.charAt(i), withDiacritics);
        return sb.toString();
    }

    public static StringBuilder deAccent(StringBuilder sb, char ch, boolean withDiacritics) {
    	String accentLetter = deaccentMap.get(ch);
    	if (accentLetter != null) {
    		if (withDiacritics || (accentLetter.length() == 1))
    			sb.append(accentLetter);
    		else
    			sb.append(accentLetter.charAt(1));
    		return sb;
    	}

        switch(ch) {
        case '\u00C6': accentLetter = "AE";  break; // &AElig;
        case '\u00E6': accentLetter = "ae";  break; // &aelig;
        case '\u0152': accentLetter = "OE";  break; // &OElig;
        case '\u0153': accentLetter = "oe";  break; // &oelig;
        case '\u00DF': accentLetter = "ss";  break; // &szlig;
        }
        if (accentLetter != null)
        	sb.append(accentLetter);
        else
        	sb.append(ch);
        return sb;
    }

    public static boolean startsWithVowel(String str) {
    	return str.length() > 0 && isVowel(str.charAt(0));
    }

    public static boolean endsWithVowel(String str) {
    	return str.length() > 0 && isVowel(str.charAt(str.length()-1));
    }

    public static boolean isVowel(char ch) {
        switch(ch) {
        case 198: // &AElig;
        case 230: // &aelig;
        case 253: // &yacute;
        case 338: // &OElig;
        case 339: // &oelig;
        	return true;
        }
        return vowelSet.contains(ch);
    }

//    public static void main(String[] args) {
//    	Charset utf8charset = Charset.forName("UTF-8");
//    	Charset iso88591charset = Charset.forName("ISO-8859-1");
//
//    	for (char[] ca : accentMap) {
//    		for (char c : ca) {
//    			String s = String.valueOf(c);
//
//    			byte[] utf8Data = utf8charset.encode(s).array(); // encode UTF-8
//    			byte[] isoData  = iso88591charset.encode(s).array(); // encode ISO-8559-1
//    			printResult(s, utf8Data, isoData);
//    		}
//    		Console.println("");
//    	}
//    }
//
//    private static void printResult(String s, byte[] utf8Data, byte[] isoData) {
//    	Console.print(s + " " + displayBytes(utf8Data) + " " + displayBytes(isoData) + " : ");
//    }
//    
//    private static String displayBytes(byte[] bytes) {
//    	String ret = "";
//    	for (byte b : bytes) {
//    		String x = Integer.toHexString(((int)b) & 0xFF);
//    		if (x.length() == 1)
//    			x = "0" + x;
//    		ret += x;
//    	}
//    	return ret;
//    }
//
//    private static void test1(String[] args) {
//    	boolean deAccent = false;
//    	String msg = null;
//    	for (String a : args) {
//    		if (a.startsWith("-")) {
//    			if (a.startsWith("-d"))
//    				deAccent = true;
//    			else if (a.startsWith("-e"))
//    				deAccent = false;
//    		}
//    		else
//    			msg = a;
//    	}
//
//    	if (msg == null) {
//    		StringBuilder sb = new StringBuilder();
//    		for (char letter : accentMapXIndex) {
//    			sb.setLength(0);
//    			for (char acc : accentMapYIndex)
//    				sb.append(acc).append(letter);
//    			msg = sb.toString();
//        		Console.println(msg);
//        		Console.println(enAccent(msg, true));
//        		Console.println(deAccent(enAccent(msg), true));
//        		Console.println("");
//    		}
//    		return;
//    	}
//    	
//    	if (deAccent) {
//    		Console.println(deAccent(msg, true));
//    	} else {
//    		Console.println(msg);
//    		Console.println(enAccent(msg, true));
//    		Console.println(deAccent(enAccent(msg), true));
//    	}
//    }

//    public static char toUpper(char ch) {
//    	switch(ch) {
//        case 230: return (char)198; // &aelig; => &AElig;
//        case 339: return (char)338; // &oelig; => &OElig;
//        case AccentedChars.a_grave: return AccentedChars.A_grave;
//        case AccentedChars.a_acute: return AccentedChars.A_acute;
//        case AccentedChars.a_circ:  return AccentedChars.A_circ;
//        case AccentedChars.a_tilde: return AccentedChars.A_uml;
//        case AccentedChars.a_uml:   return AccentedChars.E_grave;
//        case AccentedChars.e_grave: return AccentedChars.E_grave;
//        case AccentedChars.e_acute: return AccentedChars.E_acute;
//        case AccentedChars.e_circ:  return AccentedChars.E_circ;
//        case AccentedChars.e_tilde: return AccentedChars.E_tilde;
//        case AccentedChars.e_uml:   return AccentedChars.E_uml;
//        case AccentedChars.i_grave: return AccentedChars.I_grave;
//        case AccentedChars.i_acute: return AccentedChars.I_acute;
//        case AccentedChars.i_circ:  return AccentedChars.I_circ;
//        case AccentedChars.i_tilde: return AccentedChars.I_tilde;
//        case AccentedChars.i_uml:   return AccentedChars.I_uml;
//        case AccentedChars.o_grave: return AccentedChars.O_grave;
//        case AccentedChars.o_acute: return AccentedChars.O_acute;
//        case AccentedChars.o_circ:  return AccentedChars.O_circ;
//        case AccentedChars.o_tilde: return AccentedChars.O_tilde;
//        case AccentedChars.o_uml:   return AccentedChars.O_uml;
//        case AccentedChars.u_grave: return AccentedChars.U_grave;
//        case AccentedChars.u_acute: return AccentedChars.U_acute;
//        case AccentedChars.u_circ:  return AccentedChars.U_circ;
//        case AccentedChars.u_tilde: return AccentedChars.U_tilde;
//        case AccentedChars.u_uml:   return AccentedChars.U_uml;
//        case AccentedChars.c_cedil: return AccentedChars.C_cedil;
//        case AccentedChars.n_tilde: return AccentedChars.N_tilde;
//        default:                    return Character.toUpperCase(ch);
//    	}
//    }
//    
//    public static char toLower(char ch) {
//    	switch(ch) {
//    	case 198: return (char)230; // &AElig; => &aelig;
//    	case 338: return (char)339; // &OElig; => &oelig;
//        case AccentedChars.A_grave: return AccentedChars.a_grave;
//        	a_acute
//        	a_circ
//        	a_tilde
//        	a_uml
//        	e_grave
//        	e_acute
//        	e_circ
//        	e_tilde
//        	e_uml
//        	i_grave
//        	i_acute
//        	i_circ
//        	i_tilde
//        	i_uml
//        	o_grave
//        	o_acute
//        	o_circ
//        	o_tilde
//        	o_uml
//        	u_grave
//        	u_acute
//        	u_circ
//        	u_tilde
//        	u_uml
//        	c_cedil
//        	n_tilde
//        default:
//        	return Character.toLowerCase(ch);
//    	}
//    }
}
